package com.jsn.config;

import com.jsn.constants.GlobalConstants;

import com.jsn.handler.SmartAccessDeniedHandler;
import com.jsn.handler.SmartAuthExceptionEntryPoint;
import com.jsn.properties.FilterIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.web.bind.annotation.CrossOrigin;

@Configuration
@EnableResourceServer
@AllArgsConstructor
@Order(6)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    private FilterIgnoreProperties ignorePropertiesConfig;

    private RedisConnectionFactory redisConnectionFactory;
    private SmartTokenExtractor smartTokenExtractor;
    private AccessDecisionManager accessDecisionManager;
    @Lazy
    private RemoteTokenServices remoteTokenServices;

    @Bean
    public TokenStore tokenStore() {
        RedisTokenStore redisTokenStore = new RedisTokenStore(redisConnectionFactory);
        redisTokenStore.setPrefix(GlobalConstants.PROJECT_PREFIX+GlobalConstants.OAUTH_PREFIX);
        return redisTokenStore;
//        return new JwtTokenStore(jwtAccessTokenConverter());
//        return new JdbcTokenStore(dataSource);
    }


    @Override
    @SneakyThrows
    public void configure(HttpSecurity httpSecurity) {
        //允许使用iframe 嵌套，避免swagger-ui 不被加载的问题
        httpSecurity.headers().frameOptions().disable();
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>
                .ExpressionInterceptUrlRegistry registry = httpSecurity
                .authorizeRequests();
        registry.antMatchers(HttpMethod.OPTIONS, "/**").permitAll();

        registry.antMatchers("/oauth/**").permitAll();

        //对配置的url放行 不进行验证
        ignorePropertiesConfig.getUrls()
                .forEach(url -> registry.antMatchers(url).permitAll());
        registry.anyRequest().authenticated()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                        o.setAccessDecisionManager(accessDecisionManager);      // 权限判断
                        return o;
                    }
                })

                .and()
                .httpBasic()
                .and().csrf().disable();

    }



    @Override
    @CrossOrigin
    public void configure(ResourceServerSecurityConfigurer resources) {
        // String resourceIds = publicMapper.getResourceIdsByClientId(clientId);
        // //设置客户端所能访问的资源id集合(默认取第一个是本服务的资源)
        // resources.resourceId(resourceIds.split(",")[0]).stateless(true);
        // resources.resourceId("admin").stateless(true);
        DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
        UserAuthenticationConverter userTokenConverter = new SoUserAuthenticationConverter();
        accessTokenConverter.setUserTokenConverter(userTokenConverter);
        remoteTokenServices.setAccessTokenConverter(accessTokenConverter);
        resources
                .tokenStore(tokenStore())
                .tokenServices(remoteTokenServices)
                .tokenExtractor(smartTokenExtractor)
                //自定义Token异常信息,用于token校验失败返回信息
                .authenticationEntryPoint(new SmartAuthExceptionEntryPoint())
                //授权异常处理
                .accessDeniedHandler(new SmartAccessDeniedHandler());
    }
}
